/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Mozilla browser.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 2006-2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   David Hyatt <hyatt@mozilla.org> (Original Author)
 *   Nick Kreeger <nick.kreeger@park.edu> 
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#import "NSString+Utils.h"

#import <AppKit/AppKit.h>
#import "ContactsAutoCompleteDataSource.h"
#import "ContactAutoCompleteTextField.h"

#include "nsString.h"
#include "nsCRT.h"

#include "nsAbBaseCID.h"
#include "nsIAbAutoCompleteSession.h"
#include "nsIAutoCompleteResults.h"
#include "nsIAutoCompleteListener.h"

@interface ContactsAutoCompleteDataSource(Private)

-(void)initAutocompleteServices;
-(void)onDataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus;

@end

#pragma mark -

class CHAutoCompleteListener : public nsIAutoCompleteListener
{  
public:
  CHAutoCompleteListener(ContactsAutoCompleteDataSource* aDataSource)
  {
    mDataSource = aDataSource;
  }

  NS_DECL_ISUPPORTS

  NS_IMETHODIMP OnStatus(const PRUnichar* aText) { return NS_OK; }
  NS_IMETHODIMP SetParam(nsISupports *aParam) { return NS_OK; }
  NS_IMETHODIMP GetParam(nsISupports **aParam) { return NS_OK; }

  NS_IMETHODIMP OnAutoComplete(nsIAutoCompleteResults *aResults, AutoCompleteStatus aStatus)
  {
    [mDataSource onDataReady:aResults status:aStatus];
    return NS_OK;
  }

private:
  ContactsAutoCompleteDataSource* mDataSource;
};

NS_IMPL_ISUPPORTS1(CHAutoCompleteListener, nsIAutoCompleteListener)

#pragma mark -

@implementation ContactsAutoCompleteDataSource

-(id)init
{
  if ((self = [super init])) 
  {
    mGeckoResults = nil;
    mComboResults = [[NSMutableSet alloc] init];
    mIconImage = [[NSImage imageNamed:@"globe_ico"] retain];
    [self initAutocompleteServices];
    mOwnerTextField = nil;
  }
  
  return self;
}

-(id)initWithContactAutoCompleteTextField:(ContactAutoCompleteTextField*)aContactTextField
{
  if ((self = [super init]))
  {
    mGeckoResults = nil;
    mComboResults = [[NSMutableSet alloc] init];
    mIconImage = [[NSImage imageNamed:@"globe_ico"] retain];
    [self initAutocompleteServices];
    mOwnerTextField = aContactTextField;
  }
  
  return self;
}

-(void)dealloc
{
  NS_IF_RELEASE(mGeckoResults);
  NS_IF_RELEASE(mSession);
  NS_IF_RELEASE(mListener);
  
  [mIconImage release];
  [mErrorMessage release];
  [mSearchString release];
  [mComboResults release];
  [mABResults release];
  
  [super dealloc];
}

-(void)initAutocompleteServices
{
  // Start the Gecko Auto-complete servics
  NS_IF_RELEASE(mSession);
  
  nsresult rv;
  nsCOMPtr<nsIAbAutoCompleteSession> session = do_GetService(NS_ABAUTOCOMPLETESESSION_CONTRACTID, &rv);
  
#if DEBUG
  if (NS_FAILED(rv))
    NSLog(@"Could not start the AbAutoCompleteSession!!!!");
#endif
  
  mSession = session;
  NS_IF_ADDREF(mSession);
  
  mListener = (nsIAutoCompleteListener*) new CHAutoCompleteListener(self);
  NS_IF_ADDREF(mListener);
}

-(void)onDataReady:(nsIAutoCompleteResults*)aResults status:(AutoCompleteStatus)aStatus
{
  NS_IF_RELEASE(mGeckoResults);
  mGeckoResults = nsnull;
  BOOL hasResults = NO;
  
  if (aStatus == nsIAutoCompleteStatus::failed || 
      aStatus == nsIAutoCompleteStatus::ignored ||
      aStatus == nsIAutoCompleteStatus::noMatch)
  {
    [self setErrorMessage:@""];
  }
  
  else if (aStatus == nsIAutoCompleteStatus::matchFound)
  {
    mGeckoResults = aResults;
    NS_IF_ADDREF(mGeckoResults);
    
    nsCOMPtr<nsISupportsArray> items;
    mGeckoResults->GetItems(getter_AddRefs(items));
    PRUint32 count;
    items->Count(&count);
    for (unsigned int i = 0; i < count; i++)
    {
      nsCOMPtr<nsISupports> itemSupports = dont_AddRef(items->ElementAt(i));
      nsCOMPtr<nsIAutoCompleteItem> item = do_QueryInterface(itemSupports);
      nsAutoString value;
      item->GetValue(value);
      [mComboResults addObject:[NSString stringWith_nsAString:value]];
    }
    
    //XXX sort values...
    
    hasResults = YES;
  }
  
  if (hasResults || [mComboResults count] >  0)
    [mOwnerTextField onDataAvailable];
}

-(void)clearResults
{
    // clear out search data
  if (mSearchString)
    [mSearchString release];
  
  mSearchString = nil;
  NS_IF_RELEASE(mGeckoResults);
  mGeckoResults = nsnull;
  
  [mComboResults removeAllObjects];
}

-(void)startSearch
{  
  // Lookup results in the OS X AB
  ABAddressBook* addrBook = [ABAddressBook sharedAddressBook];
  ABSearchElement* firstNameElement;
  ABSearchElement* lastNameElement;
  ABSearchElement* emailElement;
  id searchValue = mSearchString;
  
  // This doesn't work...
  if ([mSearchString rangeOfString:@" "].location != NSNotFound)
    searchValue = [mSearchString componentsSeparatedByString:@" "];
  
  firstNameElement = [ABPerson searchElementForProperty:kABFirstNameProperty
                                                  label:nil 
                                                    key:nil 
                                                  value:searchValue 
                                             comparison:kABContainsSubStringCaseInsensitive];
  
  lastNameElement =  [ABPerson searchElementForProperty:kABLastNameProperty
                                                  label:nil
                                                    key:nil
                                                  value:searchValue 
                                             comparison:kABContainsSubStringCaseInsensitive];
  
  emailElement = [ABPerson searchElementForProperty:kABEmailProperty
                                              label:nil
                                                key:nil 
                                              value:searchValue 
                                         comparison:kABContainsSubStringCaseInsensitive];
  
  ABSearchElement* search = [ABSearchElement searchElementForConjunction:kABSearchOr
                                                                children:[NSArray arrayWithObjects:firstNameElement,
                                                                                                   lastNameElement, 
                                                                                                   emailElement,
                                                                                                   nil]];
  [mComboResults removeAllObjects];
  NSArray* results = [addrBook recordsMatchingSearchElement:search];
  NSEnumerator* resultsEnum = [results objectEnumerator];
  ABPerson* person = nil;
  while ((person = [resultsEnum nextObject]))
  {
    NSString* first = [person valueForProperty:kABFirstNameProperty];
    NSString* last = [person valueForProperty:kABLastNameProperty];
    NSString* fullName = [NSString stringWithFormat:@"%@ %@", first, last];
    ABMultiValue* emailAddrs = [person valueForProperty:kABEmailProperty];
    for (unsigned int i = 0; i < [emailAddrs count]; i++)
      [mComboResults addObject:[NSString stringWithFormat:@"%@ <%@>", fullName, [emailAddrs valueAtIndex:i]]];
  }
  
  // Make sure the parent text field knows that there was a change in data.
  //[mOwnerTextField onDataAvailable];
  
  if (mSession)
  {
    nsresult rv;
    nsAutoString searchString;
    [mSearchString assignTo_nsAString:searchString];
    rv = mSession->OnStartLookup(searchString.get(), mGeckoResults, mListener);
#if DEBUG
    if (NS_FAILED(rv))
      NSLog(@"Unable to perform autocomplete lookup.");
#endif
  }
  
  [mOwnerTextField onDataAvailable];
}

-(void)setSearchString:(NSString*)aSearchString
{
  if (mSearchString)
    [mSearchString release];
  
  mSearchString = [aSearchString retain];
}

-(NSString*)searchString
{
  return mSearchString;
}

-(void)setErrorMessage:(NSString*)error
{
  [self setResults:nsnull];  // releases mErrorMessage

  mErrorMessage = [error retain];
}

-(NSString*)errorMessage
{
  return mErrorMessage;
}

-(void)setResults:(nsIAutoCompleteResults*)aResults
{
  NS_IF_RELEASE(mGeckoResults);
  
  [mErrorMessage release];
  mErrorMessage = nil;
  
  mGeckoResults = aResults;
  NS_IF_ADDREF(mGeckoResults);
}

-(nsIAutoCompleteResults*)results
{
  return mGeckoResults;
}

-(int)rowCount
{
  return [mComboResults count];
}

-(id)resultForRow:(int)aRow columnIdentifier:(NSString*)aColumnIdentifier
{
  if ([aColumnIdentifier isEqualToString:@"icon"])
    return mIconImage;
  
  else if ([aColumnIdentifier isEqualToString:@"col1"]) 
  {
    return [[mComboResults allObjects] objectAtIndex:aRow];
  }

  return @"";
}

-(int)numberOfRowsInTableView:(NSTableView*)aTableView
{
  return [mComboResults count];
}

-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)aRowIndex
{
  return [self resultForRow:aRowIndex columnIdentifier:[aTableColumn identifier]];
}

@end
